package com.datang.litejvm.cmd;

import com.datang.litejvm.clz.Method;
import com.datang.litejvm.constant.ConstantMethodRefInfo;
import com.datang.litejvm.constant.ConstantPool;
import com.datang.litejvm.engin.ExecutionResult;
import com.datang.litejvm.engin.MethodArea;
import com.datang.litejvm.engin.StackFrame;

public class InvokeSpecialCmd extends TwoOperandCmd {
    public InvokeSpecialCmd(ConstantPool pool, String opCode) {
        super(pool, opCode);
    }

    @Override
    public String toString() {
        return super.toString();
    }

    /**
     * @author: 顶风少年
     * @Description: 调用实例方法；专门用来处理调用超类方法、使用方法和实例初始化方法方法。
     * 无符号数 indexbyte1 和 indexbyte2 用于构建一个当前类（§2.6）的运
     * 行时常量池的索引值，构建方式为（indexbyte1 << 8）| indexbyte2，
     * 该索引所指向的运行时常量池项应当是一个方法（§5.1）的符号引用，它包
     * 含了方法的名称和描述符，以及包含该方法的接口的符号引用。最后，如果调
     * 用的方法是 protected 的（§4.6），并且这个方法是当前类的父类成员，并
     * 且这个方法没有在同一个运行时包（§5.3）中定义过，那 objectref 所指
     * 向的对象的类型必须为当前类或者当前类的子类。
     * 只有下面所有的条件都成立的前提下，才会进行调用方法的搜索：
     *  当前类的 ACC_SUPER 标志为真（参见表 4-1，“类访问和属性修改”）。
     *  调用方法所在的类似当前类的超类。
     *  调用方法不是实例初始化方法（§2.9）。
     * 如果以上条件都成立，虚拟机将按下面规则查找实际执行的方法，假设 C 是当
     * 前类的直接父类：
     *  如果 C 中包含了名称和描述符都和要调用的实例方法一致的方法，那这个
     * 方法就会被调用，查找过程终止。
     *  另外，如果 C 有父类，查找过程将按顺序递归搜索 C 的直接父类，如果超
     * 类中能搜索到名称和描述符都和要调用的实例方法一致的方法，那这个方
     * 法就会被调用。  否则，抛出 AbstractMethodError 异常。
     * objectref 必须是一个 reference 类型的数据，在操作数栈中，objectref
     * 之后还跟随着连续 n 个参数值，这些参数的数值、数据类型和顺序都必须遵循
     * 实例方法的描述符中的描述。
     * 如果要调用的是同步方法，那与 objectref 相关的管程（monitor）将会进
     * 入或者重入，就如当前线程中同执行了 monitorenter 指令一般。
     * 如果要调用的不是本地方法，n 个 args 参数和 objectref 将从操作数栈中
     * 出栈。方法调用的时候，一个新的栈帧将在 Java 虚拟机栈中被创建出来，
     * objectref 和连续的 n 个参数将存放到新栈帧的局部变量表中，objectref
     * 存为局部变量 0，arg1 存为局部变量 1（如果 arg1 是 long 或 double 类型，
     * 那将占用局部变量 1 和 2 两个位置），依此类推。参数中的浮点类型数据在存
     * 入局部变量之前会先进行数值集合转换（§2.8.3）。新栈帧创建后就成为当
     * 前栈帧，Java 虚拟机的 PC 寄存器被设置成指向调用方法的首条指令，程序就
     * 从这里开始继续执行。
     * 如果要调用的是本地方法，要是这些平台相关的代码尚未绑定（§5.6）到虚
     * 拟机中的话，绑定动作先要完成。指令执行时，n 个 args 参数和 objectref
     * 将从操作数栈中出栈并作为参数传递给实现此方法的代码。参数中的浮点类型
     * 数据在传递给调用方法之前会先进行数值集合转换（§2.8.3）。参数传递和
     * 代码执行都会以具体虚拟机实现相关的方式进行。当这些平台相关的代码返回
     * 时：
     *  如果这个本地方法是同步方法，那与 objectref 相关的管程状态将会被
     * 更新，也可能退出了，就如当前线程中同执行了 monitorexit 指令一般。
     *  如果这个本地方法有返回值，那平台相关的代码返回的数据必须通过某种
     * 实现相关的方式转换成本地方法所定义的 Java 类型，并压入到操作数栈
     * 中
     * @date: 22:10 2022/6/16
     **/
    @Override
    public void execute(StackFrame frame, ExecutionResult result) {
        ConstantMethodRefInfo methodRefInfo = (ConstantMethodRefInfo) getConstantInfo(getIndex());
        //不实现Object中的方法
        String className = methodRefInfo.getClassName();
        if(className.equals("java/lang/Object")){
            return;
        }
        //从方法区先获取下一个方法所属的类有没有加载,然后再获取对应方法.设置到到result
        Method method = MethodArea.getInstance().getMethod(methodRefInfo);
        result.setNextAction(ExecutionResult.PAUSE_AND_RUN_NEW_FRAME);
        result.setNextMethod(method);
    }
}
